package com.cat.orm.core.db.repository.dao;

import com.cat.orm.core.base.BasePo;
import com.cat.orm.core.base.PoMapper;
import com.cat.orm.core.base.SQLGenerator;
import com.cat.orm.core.db.dao.AbstractDao;
import com.cat.orm.core.db.dao.IDao;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
 * @author Jeremy Feng
 * @date 2024/8/27 14:33
 */
public abstract class AbstractBaseDao<T extends BasePo> implements IBaseDao<T>{

    protected static final Logger log = LoggerFactory.getLogger(AbstractDao.class);

    /**
     * 基于spring提供的JdbcTemplate实现. 仅仅需要关注业务本身 不需要关注事务,连接释放等操作
     */
    protected final JdbcTemplate jdbcTemplate;

    /**
     * po.sql映射, 对注册的POJO生成sql隐射,操作时直接通过映射的sql进行操作
     */
    protected final PoMapper<T> poMapper;

    public AbstractBaseDao(JdbcTemplate jdbcTemplate) {
        Type superClass = getClass().getGenericSuperclass();
        Class<T> clazz = (Class<T>) (((ParameterizedType) superClass).getActualTypeArguments()[0]);

        this.jdbcTemplate = jdbcTemplate;
        this.poMapper = new PoMapper<>(clazz);
    }

    public AbstractBaseDao(Class<T> clazz, JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        this.poMapper = new PoMapper<>(clazz);
    }

    /**
     * 查询所有
     *
     * @see IDao#selectAll()
     */
    @Override
    public List<T> selectAll() {
        final String sql = poMapper.getSelectAll();
        final Class<T> clazz = poMapper.getRealCls();
        log.debug("selectAll sql:{}", sql);
        List<T> basePoList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<T>(clazz));
        return basePoList;
    }

    /**
     * 根据索引查询, 需要组装
     *
     * @date 2020年6月29日
     * @return
     */
    @Override
    public T selectByKey(Object value) {
        final String sql = poMapper.getSelectByKey();
        final Class<T> clazz = poMapper.getRealCls();

        log.debug("select sql:{}, objs:{}, cls:{}", sql, value, clazz);
        List<T> basePoList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<T>(clazz), value);
        if (basePoList.size() == 0) {
            return null;
        }
        if (basePoList.size() > 1) {
            log.error("Multiple pieces of data correspond to one primary key.cls:{}, sql:{},", clazz, sql);
        }
        return basePoList.get(0);
    }

    /**
     * 通过默认主键, 索引进行查询 不需要指定主键和索引, 默认通过所有索引进行查询
     */
    @Override
    public List<T> selectByIndex(Object[] value) {
        final String sql = poMapper.getSelectByIndex();
        final Class<T> clazz = poMapper.getRealCls();

        log.debug("select sql:{}, cls:{}", sql, clazz);
        List<T> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<T>(clazz), value);
        return list;
    }

    /**
     * 入库
     *
     * @date 2020年6月29日
     * @param po
     * @return
     */
    public int insert(T po) {
        final String sql = poMapper.getInsert();
        log.debug("insert sql:{}, po:{}", sql, po);
        return jdbcTemplate.update(sql, po.propValues());
    }

    public int replace(T po) {
        final String sql = poMapper.getReplace();
        log.debug("replace sql:{}", sql);
        return jdbcTemplate.update(sql, po.propValues());
    }

    public int update(T po) {
        final String sql = poMapper.getUpdate();
        log.debug("update sql:{}", sql);
        Object[] props = po.propValues();
        Object[] ids = po.keyAndIndexValues();
        Object[] objects = new Object[props.length + ids.length];
        System.arraycopy(props, 0, objects, 0, props.length);
        System.arraycopy(ids, 0, objects, props.length, ids.length);
        return jdbcTemplate.update(sql, objects);
    }

    /**
     * 根据主键, 索引删除
     */
    public int delete(T po) {
        final String sql = poMapper.getDelete();
        final Object[] indexsValue = po.keyAndIndexValues();
        log.debug("delete sql:{}, idValues:{}", sql, indexsValue);
        return jdbcTemplate.update(sql, indexsValue);
    }

    /**
     * 删除所有
     */
    public int deleteAll() {
        final String sql = poMapper.getDeleteAll();
        log.debug("deleteAll sql:{}", sql);
        return jdbcTemplate.update(sql);
    }

    /**
     * 批量添加
     */
    public int[] insertBatch(Collection<T> basePos) {
        final String sql = poMapper.getInsert();
        List<Object[]> calValues = new ArrayList<Object[]>();
        for (T basePo : basePos) {
            calValues.add(basePo.propValues());
        }
        log.debug("insertBatch sql:{}", sql);
        return jdbcTemplate.batchUpdate(sql, calValues);
    }

    /**
     * 批量删除
     */
    public int[] deleteBatch(Collection<T> basePos) {
        final String sql = poMapper.getDelete();
        List<Object[]> calValues = new ArrayList<Object[]>();
        for (T basePo : basePos) {
            calValues.add(basePo.keyAndIndexValues());
        }
        log.debug("deleteBatch sql:{}", sql);
        return jdbcTemplate.batchUpdate(sql, calValues);
    }

    /**
     * 批量修改
     */
    public int[] updateBatch(Collection<T> basePos) {
        final String sql = poMapper.getUpdate();
        List<Object[]> calValues = new ArrayList<Object[]>();
        for (T basePo : basePos) {
            //存储数据, 先添加基础值, 再添加主键索引
            Object[] props = basePo.propValues();
            Object[] ids = basePo.keyAndIndexValues();
            Object[] objects = new Object[props.length + ids.length];
            System.arraycopy(props, 0, objects, 0, props.length);
            System.arraycopy(ids, 0, objects, props.length, ids.length);
            calValues.add(objects);
        }
        log.debug("updateBatch sql:{}", sql);
        return jdbcTemplate.batchUpdate(sql, calValues);
    }

    /**
     * 通过指定的主键, 值进行查询, 自动生成
     *
     * @param props  索引字段
     * @param values 查询值 比如:select * from player where playerId = 1
     *               selectByIndex(new String[]{"playerId"}, new Object[]{1})
     */
    @Override
    public List<T> selectByIndex(String[] props, Object[] values) {
        final String tbName = poMapper.getTbName();
        final Class<T> clazz = poMapper.getRealCls();
        String sql = SQLGenerator.select(tbName, props);
        List<T> basePoList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<T>(clazz), values);
        return basePoList;
    }

    /**
     * 通过sql语句查询,在以上所有语句不能处理业务时, 使用此方法. 因为默认sql不缓存, 所以效率比以上操作相比略低.
     * @param sql
     * @return BasePo
     * @date 2020年9月7日下午4:53:28
     */
    @Override
    public Collection<T> selectBySql(String sql) {
        final Class<T> clazz = poMapper.getRealCls();
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<T>(clazz));
    }

    @Override
    public void execute(String sql) {
        final Class<T> clazz = poMapper.getRealCls();
        jdbcTemplate.execute(sql);
    }

}
